package com.chucang.shucang.ttp.nftcenter.proxy.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.chucang.shucang.common.base.exception.FastException;
import com.chucang.shucang.ttp.nftcenter.configproperty.AvataProperties;
import com.chucang.shucang.ttp.nftcenter.model.dto.*;
import com.chucang.shucang.ttp.nftcenter.model.req.*;
import com.chucang.shucang.ttp.nftcenter.proxy.AvataProxy;
import com.chucang.shucang.ttp.nftcenter.utils.AvataUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author enlai.wang 16621134798
 * @date: 2022/5/26
 */
@Slf4j
@Service
@AllArgsConstructor
public class AvataProxyImpl implements AvataProxy {

    private final AvataProperties avataProperties;
    @Resource
    private RestTemplate restTemplate;

    private static final String CREATE_ACCOUNT_URL = "/v1beta1/account";

    private static final String NFT_CLASS_DETAIL_QUERY = "/v1beta1/nft/classes/";

    private static final String NFT_PUBLISH_URL_PRE = "/v1beta1/nft/nfts/";

    private static final String DEAL_RESULT_QUERY_URL_PRE = "/v1beta1/tx/";

    private static final String NFT_TRANSFER_URL_PRE = "/v1beta1/nft/nft-transfers/";

    private static final String NFT_CREATE_URL = "/v1beta1/nft/classes";

    private static final String NFT_DESTORY_URL_PRE = "/v1beta1/nft/nfts/";

    @Override
    public CreateAccountDTO createAccount(String name, String operationId) {
        if (StringUtils.isBlank(name)) {
            throw new FastException("缺少必传参数name");
        }
        if (StringUtils.isBlank(operationId)) {
            throw new FastException("缺少必传参数operationId");
        }
        long currentTime = System.currentTimeMillis();
        // 请求body
        Map<String, Object> body = new HashMap<>();
        body.put("name", name);
        body.put("operation_id", operationId);
        // 验签
        String signature = AvataUtils.signRequest(CREATE_ACCOUNT_URL, null, body, currentTime, avataProperties.getApiSecret());
        // 请求体
        HttpHeaders headers = getHttpHeader(signature, currentTime);
        headers.setContentType(MediaType.parseMediaType("application/json"));

        HttpEntity<String> httpEntity = new HttpEntity<>(JSON.toJSONString(body), headers);

        String url = avataProperties.getAvataUrl() + CREATE_ACCOUNT_URL;
        log.info("文昌链接口请求地址===>{}", url);
        try {
            // 接口调用
            ParameterizedTypeReference<Message<CreateAccountDTO>> typeReference = new ParameterizedTypeReference<>() {
            };
            ResponseEntity<Message<CreateAccountDTO>> responseEntity;
            responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, typeReference);

            Message<CreateAccountDTO> message = responseEntity.getBody();

            assert message != null;
            CreateAccountDTO createAccountDTO = message.getData();
            // 成功
            log.info("创建链账户成功===>{}", createAccountDTO);

            return createAccountDTO;
        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("非200处理链账户创建失败===>name:{},operationId:{}", name, operationId);
            log.error("离岸账户创建失败，errMsg===>{}", e.getMessage(), e);
            throw new FastException("链账户创建失败");
        }
    }

    @Override
    public NFTOperateDTO publishNFT(NFTPublishReq nftPublishReq) {
        if (null == nftPublishReq) {
            log.info("发行NFT失败，缺少参数");
            throw new FastException("发行NFT失败，缺少参数");
        }
        if (null == nftPublishReq.getClass_id()) {
            log.info("发行NFT失败，缺少参数class_id:{}", nftPublishReq);
            throw new FastException("发行NFT失败，缺少参数class_id");
        }
        if (null == nftPublishReq.getName()) {
            log.info("发行NFT失败，缺少参数name:{}", nftPublishReq);
            throw new FastException("发行NFT失败，缺少参数name");
        }
        if (null == nftPublishReq.getOperation_id()) {
            log.info("发行NFT失败，缺少参数operation_id:{}", nftPublishReq);
        }

        String path = NFT_PUBLISH_URL_PRE + nftPublishReq.getClass_id();

        Map<String, Object> body = JSON.parseObject(JSON.toJSONString(nftPublishReq), new TypeReference<>() {
        });

        body.remove("class_id");

        long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(path, null, body, currentTime, avataProperties.getApiSecret());
        // 请求体
        HttpHeaders headers = getHttpHeader(signature, currentTime);
        headers.setContentType(MediaType.parseMediaType("application/json"));

        HttpEntity<String> httpEntity = new HttpEntity<>(JSON.toJSONString(body), headers);

        String url = avataProperties.getAvataUrl() + path;
        try {
            // 接口调用
            ParameterizedTypeReference<Message<NFTOperateDTO>> typeReference = new ParameterizedTypeReference<>() {
            };
            ResponseEntity<Message<NFTOperateDTO>> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, typeReference);

            Message<NFTOperateDTO> message = responseEntity.getBody();

            NFTOperateDTO nftOperateDTO = message.getData();
            // 成功
            log.info("发行NFT成功:{}", nftOperateDTO);

            return nftOperateDTO;
        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("非200发行NFT失败:参数为：{}", nftPublishReq);
            log.error("发行NFT失败，errMsg==>{}", e.getMessage(), e);
            throw new FastException("发行NFT失败");
        }
    }

    @Override
    public DealResultDTO queryDealResult(String taskId) {
        if (StringUtils.isBlank(taskId)) {
            log.info("上链交易结果查询失败，缺少参数taskId:{}", taskId);
            throw new FastException("上链交易结果查询失败，缺少参数taskId");
        }

        String path = DEAL_RESULT_QUERY_URL_PRE + taskId;

        long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(path, null, null, currentTime, avataProperties.getApiSecret());

        HttpHeaders headers = getHttpHeader(signature, currentTime);

        try {
            // 接口调用
            ParameterizedTypeReference<Message<DealResultDTO>> typeReference = new ParameterizedTypeReference<Message<DealResultDTO>>() {
            };
            ResponseEntity<Message<DealResultDTO>> responseEntity = restTemplate.exchange(avataProperties.getAvataUrl() + path, HttpMethod.GET, new HttpEntity<>(headers), typeReference);

            Message<DealResultDTO> message = responseEntity.getBody();
            assert message != null;
            DealResultDTO dealResultDTO = message.getData();
            log.info("上链交易结果查询===>{}", dealResultDTO);
            return dealResultDTO;

        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("非200上链交易结果查询失败");
            log.error("上链结果查询失败！errMsg===>{}", e.getMessage(), e);
            throw new FastException("上链交易结果查询失败");
        }
    }

    @Override
    public NFTOperateDTO transferNFT(NFTTransferReq nftTransferReq) {
        if (null == nftTransferReq) {
            log.info("转让NFT失败，缺少参数");
            throw new FastException("转让NFT失败，缺少参数");
        }
        if (null == nftTransferReq.getClass_id()) {
            log.info("转让NFT失败，缺少参数class_id:{}", nftTransferReq);
            throw new FastException("转让NFT失败，缺少参数class_id");
        }
        if (null == nftTransferReq.getOwner()) {
            log.info("转让NFT失败，缺少参数owner:{}", nftTransferReq);
            throw new FastException("转让NFT失败，缺少参数owner");
        }
        if (null == nftTransferReq.getNft_id()) {
            log.info("转让NFT失败，缺少参数nft_id:{}", nftTransferReq);
            throw new FastException("转让NFT失败，缺少参数nft_id");
        }
        if (null == nftTransferReq.getRecipient()) {
            log.info("转让NFT失败，缺少参数recipient:{}", nftTransferReq);
            throw new FastException("转让NFT失败，缺少参数recipient");
        }
        if (null == nftTransferReq.getOperation_id()) {
            log.info("转让NFT失败，缺少参数operation_id:{}", nftTransferReq);
            throw new FastException("转让NFT失败，缺少参数operation_id");
        }

        String path = NFT_TRANSFER_URL_PRE + nftTransferReq.getClass_id() + "/" + nftTransferReq.getOwner() + "/" + nftTransferReq.getNft_id();

        Map<String, Object> body = JSON.parseObject(JSON.toJSONString(nftTransferReq), new TypeReference<>() {
        });

        body.remove("class_id");
        body.remove("owner");
        body.remove("nft_id");

        long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(path, null, body, currentTime, avataProperties.getApiSecret());
        // 请求体
        HttpHeaders headers = getHttpHeader(signature, currentTime);
        headers.setContentType(MediaType.parseMediaType("application/json"));

        HttpEntity<String> httpEntity = new HttpEntity<>(JSON.toJSONString(body), headers);

        String url = avataProperties.getAvataUrl() + path;
        try {
            // 接口调用
            ParameterizedTypeReference<Message<NFTOperateDTO>> typeReference = new ParameterizedTypeReference<>() {
            };
            ResponseEntity<Message<NFTOperateDTO>> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, typeReference);

            Message<NFTOperateDTO> message = responseEntity.getBody();

            assert message != null;
            NFTOperateDTO nftOperateDTO = message.getData();
            // 成功
            log.info("转让NFT成功:{}", nftOperateDTO);

            return nftOperateDTO;
        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("转让NFT失败===>{}", nftTransferReq);
            log.error("转让NFT失败，errMsg==>{}", e.getMessage(), e);
            throw new FastException("转让NFT失败");
        }
    }

    @Override
    public NFTOperateDTO createNFT(NFTCreateReq nftCreateReq) {
        if (null == nftCreateReq) {
            log.info("创建NFT失败，缺少参数");
            throw new FastException("创建NFT失败，缺少参数");
        }
        if (null == nftCreateReq.getName()) {
            log.info("创建NFT失败，缺少参数name：{}", nftCreateReq);
            throw new FastException("创建NFT失败，缺少参数name");
        }
        if (null == nftCreateReq.getOwner()) {
            log.info("创建NFT失败，缺少参数owner：{}", nftCreateReq);
            throw new FastException("创建NFT失败，缺少参数owner");
        }
        if (null == nftCreateReq.getOperation_id()) {
            log.info("创建NFT失败，缺少参数operation_id：{}", nftCreateReq);
            throw new FastException("创建NFT失败，缺少参数operation_id");
        }

        Map<String, Object> body = JSON.parseObject(JSON.toJSONString(nftCreateReq), new TypeReference<>() {
        });


        long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(NFT_CREATE_URL, null, body, currentTime, avataProperties.getApiSecret());
        // 请求体
        HttpHeaders headers = getHttpHeader(signature, currentTime);
        headers.setContentType(MediaType.parseMediaType("application/json"));

        HttpEntity<String> httpEntity = new HttpEntity<>(JSON.toJSONString(body), headers);

        String url = avataProperties.getAvataUrl() + NFT_CREATE_URL;
        try {
            // 接口调用
            ParameterizedTypeReference<Message<NFTOperateDTO>> typeReference = new ParameterizedTypeReference<>() {
            };
            ResponseEntity<Message<NFTOperateDTO>> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, typeReference);

            Message<NFTOperateDTO> message = responseEntity.getBody();

            NFTOperateDTO nftOperateDTO = message.getData();
            // 成功
            log.info("创建NFT成功:{}", nftOperateDTO);

            return nftOperateDTO;
        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("创建NFT失败===>{}", nftCreateReq);
            log.error("创建NFT失败，errMsg===>{}", e.getMessage(), e);
            throw new FastException("创建NFT失败");
        }
    }


    @Override
    public NFTOperateDTO destroyNFT(NFTDestroyReq nftDestroyReq) {
        if (null == nftDestroyReq) {
            log.info("销毁NFT失败，缺少参数");
            throw new FastException("销毁NFT失败，缺少参数");
        }
        if (null == nftDestroyReq.getClass_id()) {
            log.info("销毁NFT失败，缺少参数class_id:{}", nftDestroyReq);
            throw new FastException("销毁NFT失败，缺少参数class_id");
        }
        if (null == nftDestroyReq.getOwner()) {
            log.info("销毁NFT失败，缺少参数owner:{}", nftDestroyReq);
            throw new FastException("销毁NFT失败，缺少参数owner");
        }
        if (null == nftDestroyReq.getNft_id()) {
            log.info("销毁NFT失败，缺少参数nft_id:{}", nftDestroyReq);
            throw new FastException("销毁NFT失败，缺少参数nft_id");
        }
        if (null == nftDestroyReq.getOperation_id()) {
            log.info("销毁NFT失败，缺少参数operation_id:{}", nftDestroyReq);
            throw new FastException("销毁NFT失败，缺少参数operation_id");
        }

        String path = NFT_DESTORY_URL_PRE + nftDestroyReq.getClass_id() + "/" + nftDestroyReq.getOwner() + "/" + nftDestroyReq.getNft_id();

        Map<String, Object> body = JSON.parseObject(JSON.toJSONString(nftDestroyReq), new TypeReference<>() {
        });

        body.remove("class_id");
        body.remove("owner");
        body.remove("nft_id");

        Long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(path, null, body, currentTime, avataProperties.getApiSecret());
        // 请求体
        HttpHeaders headers = getHttpHeader(signature, currentTime);
        headers.setContentType(MediaType.parseMediaType("application/json"));

        HttpEntity<String> httpEntity = new HttpEntity<>(JSON.toJSONString(body), headers);

        String url = avataProperties.getAvataUrl() + path;
        try {
            // 接口调用
            ParameterizedTypeReference<Message<NFTOperateDTO>> typeReference = new ParameterizedTypeReference<>() {
            };
            ResponseEntity<Message<NFTOperateDTO>> responseEntity = restTemplate.exchange(url, HttpMethod.DELETE, httpEntity, typeReference);

            Message<NFTOperateDTO> message = responseEntity.getBody();

            assert message != null;
            NFTOperateDTO nftOperateDTO = message.getData();
            // 成功
            log.info("销毁NFT成功===>{}", nftOperateDTO);

            return nftOperateDTO;
        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("销毁NFT失败===>{}", nftDestroyReq);
            log.error("销毁NFT失败,errMsg===>{}", e.getMessage(), e);
            throw new FastException("销毁NFT失败");
        }
    }

    @Override
    public NFTClassDTO queryNFTClassDetail(String classId) {
        if (StringUtils.isBlank(classId)) {
            throw new FastException("NFT类别详情查询失败，缺少参数classId");
        }

        String path = NFT_CLASS_DETAIL_QUERY + classId;

        long currentTime = System.currentTimeMillis();
        // 验签
        String signature = AvataUtils.signRequest(path, null, null, currentTime, avataProperties.getApiSecret());

        HttpHeaders headers = getHttpHeader(signature, currentTime);

        try {
            // 接口调用
            ParameterizedTypeReference<Message<NFTClassDTO>> typeReference = new ParameterizedTypeReference<>() {
            };
            ResponseEntity<Message<NFTClassDTO>> responseEntity = restTemplate.exchange(avataProperties.getAvataUrl() + path, HttpMethod.GET, new HttpEntity<>(headers), typeReference);

            Message<NFTClassDTO> message = responseEntity.getBody();
            assert message != null;
            NFTClassDTO nftClassDTO = message.getData();
            log.info("上链交易结果查询：{}", nftClassDTO);
            return nftClassDTO;

        } catch (Exception e) {
            // 非200都会抛异常
            // 处理异常
            log.info("非200上链交易结果查询失败");
            log.error("上链交易结果查询失败，errMsg===>{}", e.getMessage(), e);
            throw new FastException("上链交易结果查询失败");
        }
    }

    HttpHeaders getHttpHeader(String signature, Long timestamp) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("X-Api-Key", avataProperties.getApiKey());
        headers.set("X-Timestamp", timestamp.toString());
        headers.set("X-Signature", signature);

        return headers;
    }
}
